package com.zh.webcommon.common.support;

import org.apache.commons.lang3.SerializationUtils;
import org.springframework.beans.BeanUtils;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * copy对象的属性
 * 使用BeanUtils工具
 */
public final class CopyFieldUtil {

    /**
     * 深拷贝list
     * @param list 原list
     * @return 新list
     * @param <T> T
     */
    public static <T extends Serializable> List<T> copyList(List<T> list) {
        return list.stream().map(SerializationUtils::clone).collect(Collectors.toList());
    }

    public static <E, T> T copyProperties(E source, T target) {
        try {
            BeanUtils.copyProperties(source, target);
            return target;
        } catch (Exception ex) {
            throw new IllegalArgumentException("copy对象的属性时错误：" + ex.getMessage());
        }
    }

    public static <E, T> T copyPropertiesIgnoreNull(E source, T target) {
        try {
            String[] nullFieldName = NullFieldUtil.getNullFieldName(source);
            BeanUtils.copyProperties(source, target, nullFieldName);
            return target;
        } catch (Exception ex) {
            throw new IllegalArgumentException("copy对象的属性时错误：" + ex.getMessage());
        }
    }

    public static Map<String, Object> objToMap(Object obj) throws IntrospectionException, ReflectiveOperationException {
        Map<String, Object> map = new HashMap<>();
        BeanInfo beanInfo = Introspector.getBeanInfo(obj.getClass());
        PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
        for (PropertyDescriptor property : propertyDescriptors) {
            String key = property.getName();
            if (key.compareToIgnoreCase("class") == 0) {
                continue;
            }
            Method getter = property.getReadMethod();
            Object value = getter != null ? getter.invoke(obj) : null;
            map.put(key, value);
        }
        return map;
    }

    public static Object mapToObj(Map<String, Object> map, Class<?> clazz) throws IntrospectionException, ReflectiveOperationException  {
        if (map == null) return null;
        Object obj = clazz.getDeclaredConstructor().newInstance();
        BeanInfo beanInfo = Introspector.getBeanInfo(obj.getClass());
        PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
        for (PropertyDescriptor property : propertyDescriptors) {
            Method setter = property.getWriteMethod();
            if (setter != null) {
                setter.invoke(obj, map.get(property.getName()));
            }
        }
        return obj;
    }


    public static class NullFieldUtil {
        private NullFieldUtil() {
        }

        /**
         * 返回对象中的null字段名
         */
        public static <T> String[] getNullFieldName(T obj) {
            //暂存对象的全部field
            Set<Field> fieldSet = new HashSet<>();

            Class<?> aClass = obj.getClass();
            while (aClass != null && aClass != Object.class) {
                fieldSet.addAll(Arrays.asList(aClass.getDeclaredFields()));
                aClass = aClass.getSuperclass();
            }

            Object[] fieldNames = fieldSet.stream()
                    .filter(e -> {
                        try {
                            e.setAccessible(true);
                            return e.get(obj) == null;
                        } catch (IllegalAccessException ignored) {
                            return false;
                        }
                    })
                    .map(Field::getName)
                    .toArray();

            String[] names = new String[fieldNames.length];
            for (int i = 0; i < fieldNames.length; i++) {
                names[i] = (String) fieldNames[i];
            }
            return names;
        }
    }
}
